#ifndef SSY_MUTRIXBASE_H
#define SSY_MUTRIXBASE_H


#include <cassert>
#include <iostream>
#include <ostream>
#include <string>
#include <vector>

using namespace std;

template<typename ElementType>
class MatrixBase {
public:
	enum matrix_type { 
		MATRIX_DIAGONAL,
		MATRIX_COMMON,
		MATRIX_DEFAULT = MATRIX_COMMON,
		MATRIX_TRIANGULAR_UPPER, 
		MATRIX_TRIANGULAR_LOWER,
		MATRIX_TRIANGULAR_DEFAULT = MATRIX_TRIANGULAR_LOWER
	};

	MatrixBase(size_t row, size_t col) { 
		_type = MATRIX_DEFAULT;
		_row = row; _col = col; 
		_rowidx = _colidx = 0;
	}
	MatrixBase(const MatrixBase& matbase);
	virtual ~MatrixBase(void) { 
		_type = MATRIX_DEFAULT;
		_row = _col = 0; 
		_rowidx = _colidx = 0;
	}

	size_t GetCol(void) const { return _col; }
	size_t GetRow(void) const { return _row; }
	enum matrix_type GetType(void) const { return _type; }	
	const string& GetErrMsg(void) const { return _msg; }
	bool IsDiagonal(void) const;
	bool IsTriangularLower(void) const;
	bool IsTriangularUpper(void) const;

	virtual const ElementType& GetValue(size_t rowidx, size_t colidx) const = 0;
	virtual bool SetValue(size_t rowidx, size_t colidx, const ElementType& element) = 0;
	virtual void Print(void) const { cout << "undefined Print()"; }

	MatrixBase<ElementType>& operator=(const MatrixBase<ElementType>& matbase);
	MatrixBase<ElementType>& operator+=(const MatrixBase<ElementType>& matbase);
	MatrixBase<ElementType>& operator-=(const MatrixBase<ElementType>& matbase);
	MatrixBase<ElementType>& operator*=(const MatrixBase<ElementType>& matbase);
	MatrixBase<ElementType>& operator*=(const ElementType& element);

	friend MatrixBase<ElementType>& operator<<(MatrixBase<ElementType>& matbase, const ElementType& element) {
		assert(matbase.PushValue(element));
		return matbase;
	}
	friend MatrixBase<ElementType>& operator,(MatrixBase<ElementType>& matbase, const ElementType& element) {
		assert(matbase.PushValue(element));
		return matbase;
	}

protected:	
	virtual bool PushValue(const ElementType& element) = 0;
	void SetType(enum matrix_type type) { _type = type; }
	void SetErrMsg(const string& msg) { _msg = msg; }
	size_t GetRowIndex(void) const { return _rowidx; }
	size_t GetColIndex(void) const { return _colidx; }

	static const ElementType _c_zero;
	enum matrix_type _type;
	size_t _rowidx;
	size_t _colidx;

private:
	size_t _row;
	size_t _col;
	string _msg;
};

template<typename ElementType>
const ElementType MatrixBase<ElementType>::_c_zero = 0;

template<typename ElementType>
MatrixBase<ElementType>::MatrixBase(const MatrixBase& matbase) {
	_type = matbase.GetType();
	_rowidx = matbase.GetRowIndex();
	_colidx = matbase.GetColIndex();
	_row = matbase.GetRow();
	_col = matbase.GetCol();
	SetErrMsg(matbase.GetErrMsg());
}

template<typename ElementType>
bool MatrixBase<ElementType>::IsDiagonal(void) const {
	if (GetRow() == GetCol()) {
		for (size_t rIdx = 0; rIdx < GetRow(); rIdx++) {
			for (size_t cIdx = 0; cIdx < GetCol(); cIdx++) {
				if (cIdx != rIdx && _c_zero != GetValue(rIdx, cIdx))
					return false;
			}
		}
	}
	return true;
}

template<typename ElementType>
bool MatrixBase<ElementType>::IsTriangularLower(void) const {
	if (GetRow() == GetCol()) {
		for (size_t rIdx = 0; rIdx < GetRow(); rIdx++) {
			for (size_t cIdx = rIdx+1; cIdx < GetCol(); cIdx++) {
				if (_c_zero != GetValue(rIdx, cIdx))
					return false;
			}
		}
	}
	return true;
}

template<typename ElementType>
bool MatrixBase<ElementType>::IsTriangularUpper(void) const {
	if (GetRow() == GetCol()) {
		for (size_t rIdx = 0; rIdx < GetRow(); rIdx++) {
			for (size_t cIdx = 0; cIdx < rIdx; cIdx++) {
				if (_c_zero != GetValue(rIdx, cIdx))
					return false;
			}
		}
	}
	return true;
}

template<typename ElementType>
MatrixBase<ElementType>& MatrixBase<ElementType>::operator=(const MatrixBase<ElementType>& matbase) {	
	assert(GetRow() == matbase.GetRow());
	assert(GetCol() == matbase.GetCol());
	_type = matbase.GetType();
	_rowidx = matbase.GetRowIndex();
	_colidx = matbase.GetColIndex();
	_row = matbase.GetRow();
	_col = matbase.GetCol();
	SetErrMsg(matbase.GetErrMsg());
	for (size_t rIdx = 0; rIdx < GetRow(); rIdx++) {
		for (size_t cIdx = 0; cIdx < GetCol(); cIdx++) {
			assert(SetValue(rIdx, cIdx, matbase.GetValue(rIdx, cIdx)));
		}
	}
	return (*this);
}

template<typename ElementType>
MatrixBase<ElementType>& MatrixBase<ElementType>::operator+=(const MatrixBase<ElementType>& matbase) {
	assert(GetRow() == matbase.GetRow());
	assert(GetCol() == matbase.GetCol());
	ElementType element;
	for (size_t rIdx = 0; rIdx < GetRow(); rIdx++) {
		for (size_t cIdx = 0; cIdx < GetCol(); cIdx++) {
			element = GetValue(rIdx, cIdx) + matbase.GetValue(rIdx, cIdx);
			assert(SetValue(rIdx, cIdx, element));
		}
	}
	return (*this);
}

template<typename ElementType>
MatrixBase<ElementType>& MatrixBase<ElementType>::operator-=(const MatrixBase<ElementType>& matbase) {
	assert(GetRow() == matbase.GetRow());
	assert(GetCol() == matbase.GetCol());
	ElementType element;
	for (size_t rIdx = 0; rIdx < GetRow(); rIdx++) {
		for (size_t cIdx = 0; cIdx < GetCol(); cIdx++) {
			element = GetValue(rIdx, cIdx) - matbase.GetValue(rIdx, cIdx);
			assert(SetValue(rIdx, cIdx, element));
		}
	}
	return (*this);
}

template<typename ElementType>
MatrixBase<ElementType>& MatrixBase<ElementType>::operator*=(const MatrixBase<ElementType>& matbase) {
	assert(GetRow() == matbase.GetRow());
	assert(GetCol() == matbase.GetCol());
	assert(GetRow() == GetCol());
	ElementType element;
	for (size_t rIdx = 0; rIdx < GetRow(); rIdx++) {
		for (size_t cIdx = 0; cIdx < matbase.GetCol(); cIdx++) {
			element = 0;
			for (size_t tIdx = 0; tIdx < GetCol(); tIdx++) 
				element += GetValue(rIdx, tIdx) * matbase.GetValue(tIdx, cIdx);
			assert(SetValue(rIdx, cIdx, element));
		}
	}
	return (*this);
}

template<typename ElementType>
MatrixBase<ElementType>& MatrixBase<ElementType>::operator*=(const ElementType& element) {
	for (size_t rIdx = 0; rIdx < GetRow(); rIdx++) {
		for (size_t cIdx = 0; cIdx < GetCol(); cIdx++) {
			assert(SetValue(rIdx, cIdx, GetValue(rIdx, cIdx) * element));
		}
	}
	return (*this);
}


#endif // SSY_MUTRIXBASE_H



